﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Health;
using SDS.SPHealthAnalyzerRules.Delegation.Helpers;

namespace SDS.SPHealthAnalyzerRules.Delegation.Rules
{
    /// <summary>
    /// Validates that the Claims to Windows Token Service (C2WTS) service account is using Constrained Delegation with Protocol Transition
    /// </summary>
    /// <remarks>
    ///   Required by Excel, PerformancePoint, InfoPath, and Visio <see cref="http://technet.microsoft.com/en-us/library/gg502594.aspx" />
    /// </remarks>
    public class C2WTS_ConstrainedDelegation : SPHealthAnalysisRule
    {
        private Guid CorrelationToken = Guid.NewGuid();
        List<string> Errors = new List<string>();
        List<string> Remedies = new List<string>();

        public override SPHealthCategory Category { get { return SPHealthCategory.Configuration; } }
        public override SPHealthCheckErrorLevel ErrorLevel { get { return SPHealthCheckErrorLevel.Error; } }

        public override SPHealthAnalysisRuleAutomaticExecutionParameters AutomaticExecutionParameters
        {
            get
            {
                return new SPHealthAnalysisRuleAutomaticExecutionParameters()
                {
                    Schedule = SPHealthCheckSchedule.Daily,
                    Scope = SPHealthCheckScope.All,
                    ServiceType = SPFarm.Local.Services.First(s => s.Name == "c2wts").GetType(),     // run on any server running C2WTS
                    RepairAutomatically = false
                };
            }
        }

        public override SPHealthCheckStatus Check()
        {
            var outval = SPHealthCheckStatus.Failed;

            LoggingService.Write(LoggingService.Category_C2WTS, TraceSeverity.Verbose, "Executing Health Check", CorrelationToken);

            Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate { outval = doCheck(); });

            LoggingService.Write(
                LoggingService.Category_C2WTS,
                TraceSeverity.Verbose,
                String.Format("Health Check completed with outcome {0}", outval.ToString()),
                CorrelationToken);

            return outval;
        } // void Check()

        public override string Summary { get { return "DELEGATION: Claims to Windows Token Service (C2WTS) service account is not configured for constrained delegation with protocol transition"; } }
        public override string Explanation
        {
            get
            {
                return String.Format(
                    @"Claims to Windows Token Service is required for delegation by:" +
                    @"{0}InfoPath Forms Services" +
                    @"{0}Excel Services" +
                    @"{0}- http://technet.microsoft.com/en-us/library/gg502605.aspx" +
                    @"{0}Visio Services" +
                    @"{0}- http://technet.microsoft.com/en-us/library/gg502607.aspx" +
                    @"{0}PerformancePoint Services" +
                    @"{0}- http://technet.microsoft.com/en-us/library/gg502603.aspx" +
                    @"{0}SQL Server 2012 Reporting Services, Integrated Mode" +
                    @"{0}- http://msdn.microsoft.com/en-us/library/hh231678(v=sql.110).aspx" +
                    @"{0}" +
                    @"{0}The C2WTS service account must be configured to use CONSTRAINED delegation WITH protocol transition ('Use any authentication protocol')" +
                    @"{0}- http://technet.microsoft.com/en-us/library/gg502594.aspx" +
                    @"{0}" +
                    @"{0}The following errors were found:" +
                    @"{0}{1}",

                    Environment.NewLine,
                    String.Join(Environment.NewLine, Errors.ToArray())
                    ).Trim();
            }
        }
        public override string Remedy
        {
            get { 
                return String.Format(
                        @"The following corrective actions are required:" +
                        @"{0}{1}",

                        Environment.NewLine,
                        string.Join(Environment.NewLine, Remedies.ToArray())
                        );
            }
        }


        private SPHealthCheckStatus doCheck()
        {
            var outval = SPHealthCheckStatus.Failed;

            try
            {
                string SvcRunsAs_Raw = Helpers.Win32API.SvcConfigMgr.GetServiceAccount_W2CTS().Split('\\').Last();
                string SvcRunsAs = SvcRunsAs_Raw.ToLower() == "LocalSystem".ToLower()
                                 ? String.Format(@"{0}$", SPServer.Local.Name.ToUpper())
                                 : SvcRunsAs_Raw;

                var DelegationSettings = Helpers.AD_Delegation.GetUser(SvcRunsAs);

                if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.NoDelegation)
                {
                    Errors.Add(String.Format(@"C2WTS using service account [{0}] is not configured for delegation", SvcRunsAs));
                    Remedies.Add(String.Format(@"Enable delegation by adding an SPN to the service account (SPN does not need to be valid)"));
                    Remedies.Add(String.Format(@"Configure account to use CONSTRAINED delegation WITH protocol transition ('Use any authentication protocol')"));
                }
                else if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.Unconstrained)
                {
                    Errors.Add(String.Format(@"C2WTS using service account [{0}] is not configured for constrained delegation", SvcRunsAs));
                    Remedies.Add(String.Format(@"Reconfigure delegation to use CONSTRAINED delegation WITH protocol transition ('Use any authentication protocol')"));
                }
                else if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.Constrained_KerbOnly)
                {
                    Errors.Add(String.Format(@"C2WTS using service account [{0}] is not configured for constrained delegation with protocol transition", SvcRunsAs));
                    Remedies.Add(String.Format(@"Reconfigure constrained delegation to use protocol transition ('Use any authentication protocol')"));
                }

                outval = Errors.Any() ? SPHealthCheckStatus.Failed : SPHealthCheckStatus.Passed;


            } // try
            catch (Exception ex)
            {
                LoggingService.Write(
                    LoggingService.Category_C2WTS,
                    TraceSeverity.High,
                    String.Format("Health Check encountered the following exception: {0}", ex.Message),
                    CorrelationToken);
            }

            return outval;
        } // doCheck()

    } // class
} // namespace
